﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Management.Automation;
using System.Management.Automation.Host;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;


namespace ExcelPoSH
{
    // PShost derived class
    // needed to add it for custom Write-Host support
    
    public sealed class ExcelPSHost: PSHost
    {
        private Guid mInstanceId;
        private Version mVersion;
        private string mName;
        private CultureInfo mCurrentCulture;
        private CultureInfo mCurrentUICulture;
        private const string mPrivateData = "Private data.";
        private ExcelPSHostUserInterface mUserInterface;
        private Excel.Application app;

        public ExcelPSHost(Excel.Application app): base()
        {
            mInstanceId = Guid.NewGuid();
            mVersion = new Version(0, 1, 0, 0);
            mName = "Excel.Host";
            mCurrentCulture = new CultureInfo("en-US");
            mCurrentUICulture = new CultureInfo("en-US");
            mUserInterface = new ExcelPSHostUserInterface(app);

            this.app = app;
        }
        public override System.Globalization.CultureInfo CurrentCulture
        {
            get { return mCurrentCulture; }
        }

        public override System.Globalization.CultureInfo CurrentUICulture
        {
            get { return mCurrentUICulture; }
        }

        public override void EnterNestedPrompt()
        {
            throw new NotImplementedException();
        }

        public override void ExitNestedPrompt()
        {
            throw new NotImplementedException();
        }

        public override Guid InstanceId
        {
            get { return mInstanceId; }
        }

        public override string Name
        {
            get { return mName; }
        }

        public override void NotifyBeginApplication()
        {
            throw new NotImplementedException();
        }

        public override void NotifyEndApplication()
        {
            throw new NotImplementedException();
        }

        public override PSObject PrivateData
        {
            get
            {
                return PSObject.AsPSObject(mPrivateData);
            }
        }

        public override void SetShouldExit(int exitCode)
        {
            throw new NotImplementedException();
        }

        public override PSHostUserInterface UI
        {
            get { return mUserInterface; }
        }

        public override Version Version
        {
            get { return mVersion; }
        }
    }

    // PSHostUserInterface derived class
    // contains custom UI methods

    public sealed class ExcelPSHostUserInterface : PSHostUserInterface
    {
        // Write-Host will write to Excel.Application.ActiveCell
        
        private Excel.Application app;
        private ExcelPSHostRawUserInterface mRawUI;

        public ExcelPSHostUserInterface(Excel.Application app)
        {
            this.app = app;
            mRawUI = new ExcelPSHostRawUserInterface();
        }
        
        public override Dictionary<string, PSObject> Prompt(
            string caption, string message,            
            Collection<FieldDescription> descriptions)
        {
            if (app != null)
            {
                Dictionary<string, PSObject> dict =
                    new Dictionary<string, PSObject>();

                foreach (FieldDescription fieldDescription in descriptions)
                {
                    Object retVal = app.InputBox(
                        fieldDescription.Name, "Parameter missing",
                        Missing.Value, Missing.Value, Missing.Value,
                        Missing.Value, Missing.Value, 2);

                    // If user clicks Cancel InputBox returns bool
                    // and it is always false
                    // else it returns the entered value

                    if (retVal is bool)
                    {
                        dict.Add(fieldDescription.Name, null);
                    }
                    else
                    {
                        dict.Add(fieldDescription.Name, 
                            new PSObject(retVal.ToString()));
                    }
                }

                return dict;
            }
            else
            {
                return null;
            }
        }

        public override int PromptForChoice(
            string caption, string message, 
            Collection<ChoiceDescription> choices, 
            int defaultChoice)
        {
            throw new NotImplementedException();
        }

        public override PSCredential PromptForCredential(
            string caption, string message, string userName, 
            string targetName, PSCredentialTypes allowedCredentialTypes, 
            PSCredentialUIOptions options)
        {
            throw new NotImplementedException();
        }

        public override PSCredential PromptForCredential(
            string caption, string message, 
            string userName, string targetName)
        {
            throw new NotImplementedException();
        }

        public override PSHostRawUserInterface RawUI
        {
            get { return mRawUI; }
        }

        public override string ReadLine()
        {
            if (app != null)
            {
                object retVal = app.InputBox(
                    "Enter a line of text", "Read Line",
                    Missing.Value, Missing.Value, Missing.Value,
                    Missing.Value, Missing.Value, 2);

                // If user clicks Cancel InputBox returns bool
                // and it is always false
                // else it returns the entered string

                if (retVal is bool)
                {
                    return null;
                }
                else
                {
                    return retVal.ToString(); 
                }
            }
            else
            {
                return null;
            }
        }

        public override System.Security.SecureString ReadLineAsSecureString()
        {
            System.Security.SecureString retVal = 
                new System.Security.SecureString();

            foreach (char c in ReadLine())
            {
                retVal.AppendChar(c);
            }

            return retVal;
        }

        // use the colorless write method

        public override void Write(
            ConsoleColor foregroundColor, 
            ConsoleColor backgroundColor, string value)
        {
            Write(value);
        }

        // Write the string to console output textbox

        public override void Write(string value)
        {
            if (value != "\n")
            {
                Globals.ThisAddIn.console.SetText(value, true);
            }
        }

        public override void WriteDebugLine(string message)
        {
            throw new NotImplementedException();
        }

        public override void WriteErrorLine(string value)
        {
            throw new NotImplementedException();
        }

        // Write-Host uses this method adding '\n' to value
        // so call Write(value)

        public override void WriteLine(string value)
        {
            if (app.ActiveCell != null)
            {
                Write(value);
            }
        }

        public override void WriteProgress(
            long sourceId, ProgressRecord record)
        {
            throw new NotImplementedException();
        }

        public override void WriteVerboseLine(string message)
        {
            throw new NotImplementedException();
        }

        public override void WriteWarningLine(string message)
        {
            throw new NotImplementedException();
        }
    }

    // Write-Host didn't work without adding this class
    // and setting BufferSize, BackgroundColor and ForegroundColor

    public sealed class ExcelPSHostRawUserInterface : PSHostRawUserInterface
    {
        private Size mBufferSize = new Size(80, 80);
        
        public override ConsoleColor BackgroundColor
        {
            get
            {
                return ConsoleColor.White;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override Size BufferSize
        {
            get
            {
                return mBufferSize;
            }
            set
            {
                mBufferSize = value;
            }
        }

        public override Coordinates CursorPosition
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override int CursorSize
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override void FlushInputBuffer()
        {
            throw new NotImplementedException();
        }

        public override ConsoleColor ForegroundColor
        {
            get
            {
                return ConsoleColor.Black;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override BufferCell[,] GetBufferContents(Rectangle rectangle)
        {
            throw new NotImplementedException();
        }

        public override bool KeyAvailable
        {
            get { throw new NotImplementedException(); }
        }

        public override Size MaxPhysicalWindowSize
        {
            get { throw new NotImplementedException(); }
        }

        public override Size MaxWindowSize
        {
            get { throw new NotImplementedException(); }
        }

        public override KeyInfo ReadKey(ReadKeyOptions options)
        {
            throw new NotImplementedException();
        }

        public override void ScrollBufferContents(
            Rectangle source, Coordinates destination, 
            Rectangle clip, BufferCell fill)
        {
            throw new NotImplementedException();
        }

        public override void SetBufferContents(
            Rectangle rectangle, BufferCell fill)
        {
            throw new NotImplementedException();
        }

        public override void SetBufferContents(
            Coordinates origin, BufferCell[,] contents)
        {
            throw new NotImplementedException();
        }

        public override Coordinates WindowPosition
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override Size WindowSize
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override string WindowTitle
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }
    }

}
